import { get, isFunction } from "lodash-es";
import { createDiscreteApi } from "naive-ui";
import { AuthStorage } from "@/storage/auth.storage";
import axios, {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  Canceler,
  InternalAxiosRequestConfig,
} from "axios";
import {
  BaseReponse,
  RequestConfig,
  ConfigEnum,
  CustomRespEnum,
  UploadFileParams,
  UploadFileCallBack,
} from "./types";

import { TOKEN_KEY } from "@/constants/auth";
import { HTTP_STATUS } from "@/constants/request";
import { BusinessException } from "@/exceptions/business.exception";
import type { MessageReactive } from "naive-ui";
import { useUserStore } from "@/store/modules/user.store";
import { router } from "@/router";
import * as RouterNames from "@/router/routerNames";

const { message, notification } = createDiscreteApi([
  "message",
  "notification",
]);

class Request {
  private constructor() {
    this.axiosInstace = axios.create({
      timeout: 60000,
    });
    this.initInterceptor();
  }

  static instance: Request | null = null;

  axiosInstace: AxiosInstance;

  globalLoading: number = 0;

  unauth: number = 0;

  loadingInstance: MessageReactive | null = null;

  /***
   * 存放取消方法的映射
   */
  cancelRequestSourceMap: Map<String, Canceler> = new Map();

  static getInstance(): Request {
    if (this.instance == null) {
      this.instance = new Request();
    }
    return this.instance;
  }

  /***
   * 生成每个请求的唯一key
   */
  private getPendingKey(config: AxiosRequestConfig): string {
    const { url, method, params, data } = config;
    let requestData: Record<string, any> = {};
    if (typeof data === "string") {
      requestData = JSON.parse(data);
    } else {
      requestData = data;
    }
    const key = [
      url,
      method,
      JSON.stringify(params),
      JSON.stringify(requestData),
    ].join("&");

    return key;
  }

  /***
   * 存储每个请求的唯一cancel回调
   */
  private addPending(config: AxiosRequestConfig): void {
    const pendingKey = this.getPendingKey(config);
    config.cancelToken =
      config.cancelToken ||
      new axios.CancelToken((cancel) => {
        if (!this.cancelRequestSourceMap.has(pendingKey)) {
          this.cancelRequestSourceMap.set(pendingKey, cancel);
        }
      });
  }

  /**
   * 取消所有请求
   */
  cancelAllRequests() {
    this.cancelRequestSourceMap.forEach((cancel) => {
      cancel();
    });
    this.cancelRequestSourceMap.clear();
  }

  /***
   * 删除重复请求
   */
  private removePending(config: AxiosRequestConfig): void {
    const pendingKey = this.getPendingKey(config);
    if (this.cancelRequestSourceMap.has(pendingKey)) {
      const cancelToken = this.cancelRequestSourceMap!.get(pendingKey);
      cancelToken!(pendingKey);
      this.cancelRequestSourceMap.delete(pendingKey);
    }
  }

  /**
   * 初始化拦截器
   */
  initInterceptor() {
    this.axiosInstace.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        const token = AuthStorage.getAuthToken();
        if (token) {
          config.headers[TOKEN_KEY] = token;
          config.headers[ConfigEnum.TOKEN] = token;
        }

        return config;
      }
    );

    this.axiosInstace.interceptors.response.use(
      (response: AxiosResponse<BaseReponse<any>>) => {
        const httpStatus = response.status;
        if (httpStatus === HTTP_STATUS.OK) {
          const customRespCode = get(response, "data.code");
          if (
            customRespCode !== CustomRespEnum.SUCCESS &&
            customRespCode !== CustomRespEnum.SUCCESS2
          ) {
            // 接口业务错误
            return Promise.reject(new BusinessException(response));
          }
          return response;
        } else {
          // Axios 错误
          return Promise.reject(response);
        }
      }
    );
  }

  send<T = any>(config: RequestConfig): Promise<BaseReponse<T>> {
    // 先删除重复请求
    this.removePending(config);
    this.addPending(config);
    if (!config?.hideLoading) {
      this.showLoading(config?.loadingText as string);
    }
    return new Promise((resolve, reject) => {
      this.axiosInstace
        .request(config)
        .then((res: AxiosResponse<BaseReponse<T>>) => {
          this.removePending(res.config);
          if (!config?.hideLoading) {
            this.hideLoading();
          }
          resolve(res.data);
        })
        .catch((err: any) => {
          console.log(JSON.stringify(err));
          if (err?.config) {
            this.removePending(err.config);
          }
          if (!config?.hideLoading) {
            this.hideLoading();
          }

          if (err instanceof AxiosError) {
            console.log("AxiosError");
            if (err.name === "CanceledError") {
              console.log("取消重复请求");
            } else {
              const httpStatus = err.response?.status;
              if (httpStatus === HTTP_STATUS.HTTP_UNAUTHORIZED) {
                // 401;
                const errorMsg =
                  err.response?.data?.message || "系统繁忙，请稍后再试";
                this.showErrorMsg(errorMsg);
                // 取消所有请求
                this.cancelAllRequests();
                // 重定向到到登录
                const authStore = useUserStore();
                authStore.clearAll();

                router.replace({
                  name: RouterNames.LOGIN_PAGE.NAME,
                });
                return;
              } else {
                if (!config?.hideError) {
                  const errorMsg =
                    err.response?.data?.message || "系统繁忙，请稍后再试";
                  this.showErrorMsg(errorMsg);
                }
              }
            }
          } else if (err instanceof BusinessException) {
            if (!config?.hideError) {
              const errorMsg =
                err.errorData.data?.message || "系统繁忙，请稍后再试";
              this.showErrorMsg(errorMsg);
            }
          } else {
            if (!config?.hideError) {
              const errorMsg = "系统繁忙，请稍后再试";
              this.showErrorMsg(errorMsg);
            }
          }

          reject(err);
        });
    });
  }

  uploadFile<T = any>(
    config: RequestConfig,
    params: UploadFileParams,
    callback?: UploadFileCallBack
  ) {
    const formData = new window.FormData();
    const customFilename = params.name || "file";

    if (params.filename) {
      formData.append(customFilename, params.file, params.filename);
    } else {
      formData.append(customFilename, params.file);
    }
    config.baseURL = import.meta.env.VITE_UPLOAD_BASE_URL || "/jeecgboot";
    if (params.data) {
      Object.keys(params.data).forEach((key) => {
        const value = params.data![key];
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item);
          });
          return;
        }

        formData.append(key, params.data![key]);
      });
    }
    this.showLoading("Uploading...");
    return this.axiosInstace
      .request<T>({
        ...config,
        method: "POST",
        data: formData,
        headers: {
          "Content-type": "'multipart/form-data;charset=UTF-8'",
          ignoreCancelToken: true,
        },
      })
      .then((res: any) => {
        if (callback?.success && isFunction(callback?.success)) {
          callback?.success(res?.data);
        } else if (callback?.isReturnResponse) {
          return Promise.resolve(res?.data);
        } else {
          if (res.data.success == true && res.data.code == 200) {
            message.success(res.data.message);
          } else {
            message.error(res.data.message);
          }
        }
      })
      .finally(() => {
        this.hideLoading();
      });
  }

  showErrorMsg(errorMsg: string) {
    console.log(errorMsg);
    // notification.error({
    //   message: "提示",
    //   description: errorMsg,
    // });
    notification.error({
      content: "温馨提示",
      meta: errorMsg,
      duration: 2500,
      keepAliveOnHover: true,
    });
  }

  showLoading(loadingText: string) {
    // 展示loading
    if (this.globalLoading === 0) {
      this.loadingInstance = message.loading(loadingText ?? "正在加载中...", {
        duration: 0,
      });
    }
    this.globalLoading += 1;
  }

  hideLoading() {
    this.globalLoading -= 1;
    if (this.globalLoading <= 0) {
      this.globalLoading = 0;
      // 隐藏loading
      if (this.loadingInstance) {
        this.loadingInstance.destroy();
        this.loadingInstance = null;
      }
    }
  }
}

export default Request;
